﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace LearningBubbles
{
    class World
    {
        int current1;

        public int Current1
        {
            get { return current1; }
            set { current1 = value; }
        }
        bool completed = false;

        public bool Completed
        {
            get { return completed; }
            set { completed = value; }
        }
        List<Element> currentElements;
        List<Element> elementsToBeAdded;
        Assignment assignment;

        public Assignment Assignment
        {
            get { return assignment; }
            set { assignment = value; }
        }
        List<Element> elementsToBeRemoved;
        Interaction interaction;
        Player[] players;
        Portal portal;

        public Portal Portal
        {
            get { return portal; }
            set { portal = value; }
        }

        public Player[] Players
        {
            get { return players; }
            set { players = value; }
        }

        internal List<Element> CurrentElements
        {
            get { return currentElements; }
            set { currentElements = value; }
        }

        public World(int number)
        {
            assignment = new Assignment(number, InteractionType.GRAB, AssignmentType.QUIZ);
            SetInteraction(assignment.IType);
            players = new Player[1];
            players[0] = new Player(0, "Horse");
            elementsToBeAdded = new List<Element>();
            elementsToBeRemoved = new List<Element>();
            currentElements = new List<Element>();
            currentElements = assignment.Elements.ElementAt(current1);
            portal = new Portal(Textures.KinectRGBVideo.Width - (Textures.PortalTex.Width/2), (Textures.PortalTex.Height/2), 20);
        }
        
        /// <summary>
        /// Updates updates all the elements of the game.
        /// </summary>
        /// <param name="gametime"></param>
        /// <param name="kinectControl"></param>
        public void Update(GameTime gametime, KinectControl kinectControl, BubbleObservable bo)
        {
            if (Current1 >= Assignment.Elements.Count)
            {
                completed = true;
            }

            foreach (Element e in currentElements)
            {
                e.Move(gametime);
                interacting(0,e,bo);
            }
            foreach (Element e in elementsToBeAdded)
            {
                currentElements.Add(e);
            }
            elementsToBeAdded.Clear();
            foreach (Element e in elementsToBeRemoved)
            {
                currentElements.Remove(e);
            }
            elementsToBeRemoved.Clear();


            if (currentElements.Count == 0)
            {
                Current1++;
                if(current1 < assignment.Elements.Count)
                    currentElements = assignment.Elements.ElementAt(current1);
            }

        }

        /// <summary>
        /// Checks whether the element is outside the
        /// game area.
        /// </summary>
        /// <param name="e"></param>
        /// <returns>True, if element is outside, false if inside</returns>
        public bool OutsideBounds(Element e)
        {
            if (e.Position.X > Textures.KinectRGBVideo.Width
                || e.Position.X < 0
                || e.Position.Y > Textures.KinectRGBVideo.Height
                || e.Position.Y < 0)
                return true;
            else return false;
        }

        /// <summary>
        /// Interacts for the current player
        /// with the chosen interaction method.
        /// </summary>
        /// <param name="playerNum">Current player</param>
        /// <param name="kinectControl">Kinect controller</param>
        public void interacting(int playerNum, Element e, BubbleObservable bo)
        {
            for (int i = 0; i < players[playerNum].Hands.Length; i++)
            {
                if (PlayerData.HandGrabs[i,playerNum])
                {
                    if (!players[playerNum].Hands[i].HandInteracts) players[playerNum].Hands[i].HandInteracts = true;
                }
                else if (!PlayerData.HandGrabs[i,playerNum])
                {
                    if (players[playerNum].Hands[i].HandInteracts)
                    {
                        CheckElementPlacedInPortal(currentElements, e, playerNum, bo);
                        interaction.ReleaseElement(e, players, playerNum, i);
                    }
                }
                if (players[playerNum].Hands[i].HandInteracts) interaction.Interact(e, PlayerData.HandPosition[i,playerNum], players, playerNum, i);
            }
        }

        /// <summary>
        /// Checks if the element is grabbed by the player
        /// and if the element is placed above the portal.
        /// </summary>
        /// <param name="e"></param>
        /// <param name="playerNum"></param>
        public void CheckElementPlacedInPortal(List<Element> eList, Element e, int playerNum, BubbleObservable bo)
        {
            if (e.GrabbedBy == playerNum)
            {
                if (GetDistance(e.Position, new Vector2(portal.x,portal.y)) - portal.radius <= 0)
                {
                    players[playerNum].CollectedElements.Add(e);
                    bo.UpdateCollectedElements(players[playerNum]);
                    foreach (Element e1 in eList)
                    {
                        elementsToBeRemoved.Add(e1);
                    }
                }
            }            
        }

        /// <summary>
        /// Returns the distance between two coordinates
        /// in the game.
        /// </summary>
        /// <param name="point1"></param>
        /// <param name="point2"></param>
        /// <returns></returns>
        public int GetDistance(Vector2 point1, Vector2 point2)
        {
            double distance = Math.Sqrt(Math.Pow((point2.X - (point1.X)), 2) + Math.Pow((point2.Y - point2.Y), 2));
            if (distance < 0) distance -= 2 * distance;
            return (int)distance;
        }

        /// <summary>
        /// Sets the interaction type of the game
        /// </summary>
        /// <param name="iType">The way the player has got to interact with the elements</param>
        public void SetInteraction(InteractionType iType)
        {
            switch (iType)
            {
                case InteractionType.GRAB:
                    interaction = new InteractionGrab();
                    break;
                case InteractionType.GESTURE:
                    interaction = new InteractionGesture();
                    break;
            }
        }

    }
}
